#ifndef GAMEMAP_H_
#define GAMEMAP_H_

#include "Object.h"
#include "Resource.h"
#include "Point3.h"
#include "Units/Unit.h"

#include <set>
#include <vector>
#include <queue>
#include <map>

namespace OOInterface {

typedef std::set< ConstUnitPtr > ConsumableResourceSet;

class GameMap: public Object
{
public:
	/**
	 * GameMap( ... ):
	 * Constructs and initializes a GameMap with the specified widths and heights. All
	 * values default to zero.
	 *
	 * @param width The width of the GameMap.
	 * @param height The height of the GameMap.
	 */
	GameMap( unsigned int width, unsigned int height );

	//Copy Constructor
	GameMap( const GameMap& g );
	virtual ~GameMap();


	/**
	 * getElevation( ... ):
	 * Returns the height at the specified (x,y) pair. This is useful
	 * in determining slopes of the surrounding terrain to determine whether
	 * a building can be built at that location or if a unit can traverse
	 * that location.
	 *
	 * Additionally, any value returned < getSeaLevel() is below the water line.
	 * This implies additional constraints on unit movement whether they be
	 * strict land or water units.
	 *
	 * @param x the x coordinate to be checked
	 * @param y the y coordinate to be checked
	 *
	 * @return The elevation at the (x,y) pair on the map
	 *
	 * @throws Throws an exception if the (x,y) pair is out of bounds.
	 */
	float getElevation( unsigned int x, unsigned int y ) const;

	/**
	 * getSeaLevel():
	 * Simple convenience method when trying to determine whether
	 * the elevation of game map is below or above sea level.
	 * Always returns 0.0f.
	 *
	 * @return the value of sea level for this Game Map.
	 */
	inline float getSeaLevel() const { return 0.0f; }

	/**
	 * isWaterMap() :
	 * A convenience method to check whether the map is
	 * primarily water or not. This is useful in deciding
	 * whether to focus on naval or land units.
	 *
	 * Implies: 50% > of the GameMap tiles are under seaLevel;
	 *
	 * @return Whether the GameMap is a water map or not.
	 */
	inline bool isWaterMap() const { return !isLandMap(); }

	/**
	 * isLandMap() :
	 * A convenience method to check whether the map is
	 * primarily land or not. This is useful in deciding
	 * whether to focus on naval or land units.
	 *
	 * Implies: 50% > of the GameMap tiles are above seaLevel;
	 *
	 * @return Whether the GameMap is a land map or not.
	 */
	inline bool isLandMap() const { return _landTiles >= 0; }


	inline float getLandWaterRatio() const { return (_landTiles == 0)? 0.5f : 0.5f + (_width*_height) / _landTiles; }

	/**
	 * getGravity():
	 * The gravitational constant for this map. This is useful for determining
	 * speed of travel for units if gravity effects them.
	 *
	 * @return The gravitational constant for the map.
	 */
	inline float getGravity() const { return _gravity; }

	/**
	 * getTidalStrength():
	 * The strength of the tides on this map. This can be used for determining
	 * how much energy is generated by tidal generators, etc.
	 *
	 * @return the strength of tides on the map.
	 */
	inline float getTidalStrength() const { return _tidalStrength; }

	/**
	 * getMaxWindSpeed():
	 * The maximum sustained winds this map will produce. The actual value of
	 * wind speed can fluctuate from minWindSpeed() to MaxWindSpeed(). This can
	 * be used to determine how much energy will be generated from windmills, etc.
	 *
	 * @return The maximum wind speed of this map.
	 */
	inline float getMaxWindSpeed() const { return _maxWindSpeed; }

	/**
	 * getMinWindSpeed():
	 * The minimum sustained winds this map will produce. The actual value of
	 * wind speed can fluctuate from minWindSpeed() to MaxWindSpeed(). This can
	 * be used to determine how much energy will be generated from windmills, etc.
	 *
	 * @return The minimum wind speed of this map.
	 */
	inline float getMinWindSpeed() const { return _minWindSpeed; }

	/**
	 * getWidth():
	 * The width of the map in GameMap units.
	 *
	 * @return the width of the map.
	 */
	inline unsigned int getWidth() const { return _width; }

	/**
	 * getHeight():
	 * The height of the map in GameMap units.
	 *
	 * @return the height of the map.
	 */
	inline unsigned int getHeight() const { return _height; }

	/**
	 * getResourceDensity( ... ):
	 * Check the density of a specified resource at that location (x,y).
	 * This method is to be used for resources that will produce an indefinite s
	 * upply of the specified resource. See getConsumableResources() for
	 * information about finite resource supplies.
	 *
	 * @param resource The resource that is being queried.
	 * @param x the x coordinate of the desired location
	 * @param y the y coordinate of the desired location
	 * @return The density of the Resource at the specified location. This value's
	 * meaning differs based upon the Resource being queried.
	 *
	 * @throws Throws an exception if the (x,y) pair is out of bounds.
	 */
	float getResourceDensity( const Resource& resource, unsigned int x, unsigned int y ) const;

	/**
	 * getConsumableResources():
	 * Get a set of all the consumable resources available on the map.
	 * Consumable resources are effectively gaia (neutral) controlled units
	 * that reside on the GameMap to supply resources. (e.g., Trees, animals, etc. )
	 *
	 * @return the set of all consumable resources available
	 */
	const ConsumableResourceSet& getConsumableResources( ) const;

	/**
	 * setGravity( ... ):
	 * Sets the gravity of the GameMap.
	 *
	 * @param gravity The new gravitational constant of the map.
	 */
	void setGravity( float gravity ) { _gravity = gravity; }

	/**
	 * setTidalStrength( ... ):
	 * Sets the tidal strength of the GameMap.
	 *
	 * @param tides The new tidal strength on the map.
	 */
	void setTidalStrength( float tides ) { _tidalStrength = tides; }

	/**
	 * setMaxWindSpeed( ... ):
	 * Sets the max wind speed attainable on the GameMap.
	 *
	 * @param maxWind The maximum sustainable winds on the map.
	 */
	void setMaxWindSpeed( float maxWind ) { _maxWindSpeed = maxWind; }

	/**
	 * setMinWindSpeed( ... ):
	 * Sets the min. wind speed attainable on the GameMap.
	 *
	 * @param minWind The minimum sustainable winds on the map.
	 */
	void setMinWindSpeed( float minWind ) { _minWindSpeed = minWind; }

	/**
	 * setElevation( ... ):
	 * Set the elevation of the game map at the location (x,y) with the value height.
	 *
	 * @param x The x coordinate of the location
	 * @param y The y coordinate of the location
	 * @param height The desired height of the (x,y) location.
	 *
	 * @throws Throws an exception if the (x,y) pair is out of bounds.
	 */
	void setElevation( unsigned int x, unsigned int y, float height );

	/**
	 * setResourceDensity( ... ):
	 * Sets the resource density at the location for a specified location.
	 *
	 * @param r The resource density map that is going to be modified
	 * @param x The x coordinate of the location
	 * @param y The y coordinate of the location
	 * @param density The density of the specified resource at the location (x,y)
	 *
	 * @throws Throws an exception if the (x,y) pair is out of bounds.
	 */
	void setResourceDensity( const Resource& r, unsigned int x, unsigned int y, float density );

	/**
	 * addConsumableResource( ... )
	 * Adds a consumable resource to the GameMap. The resource being added
	 * already tracks all of its positional data as well as resource information.
	 *
	 * @param resource The consumable resource being added to the map.
	 */
	void addConsumableResource( ConstUnitPtr resource );

	/**
	 * removeConsumableResource( ... ):
	 * Removes a previously existing consumable resource from the GameMap. This can
	 * occur if the resource is consumed, destroyed, etc.
	 *
	 * @param resource The consumable resource beign removed from the map.
	 */
	void removeConsumableResource( ConstUnitPtr resource );

	virtual std::string toString() const { return std::string("GameMap"); }

private:
	unsigned int _width;	// Game map width
	unsigned int _height;	// Game map height

	int _landTiles;

	float _gravity;

	float _tidalStrength;

	float _maxWindSpeed;
	float _minWindSpeed;

	ConsumableResourceSet _consumableResources;

	float* _elevation;

	typedef std::map< int, float > DensityMap;

	typedef boost::shared_ptr< DensityMap> DensityMapPtr;

	typedef std::map< Resource, DensityMapPtr > ResourceDensityMap;
	ResourceDensityMap _density;

	//Convenience method to map (x,y) pairs into a single int
	inline unsigned int convertToIndex( unsigned int x, unsigned int y ) const { return _width*y + x; }

};

}

#endif /*GAMEMAP_H_*/
